#ifndef DESCENT_DETAIL_CELL_HPP_INCLUDED
#define DESCENT_DETAIL_CELL_HPP_INCLUDED

/**
 * @file 
 */

#include <algorithm>
#include <cstdint>
#include <map>
#include <vector>
#include <boost/array.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/static_assert.hpp>
#include <mocha/math.hpp>
#include <mocha/memory.hpp>
#include <mocha/pointers.hpp>
#include <mocha/region.hpp>
#include "descent/pointers.hpp"

/**
 * Cell Organization (Top)
 *
 *   Px : point
 *   Ex : edge
 * ExPy : edge Point
 *   Sx : subdivision
 *    o : origin (relative to width)
 *    w : width (logarithmic)
 * 
 *       |<-------w------->|
 *    P2 E1P1    E1     E1P0 P1
 *  E2P0 o--------+--------+ E0P1
 *       |        |        |
 *       |   S2   |   S1   |
 *       |        |        |              perspective
 *    E2 +--------+--------+ E0                +
 *       |        |        |
 *       |   S3   |   S0   |
 *       |        |        |
 *  E2P1 +--------+--------+ E0P0
 *    P3 E3P0    E3     E3P1 P0
 *
 * Cell Layering (Side)
 *
 *   L x : layer
 *   L 0 : primary layer
 *     w : width (logarithmic)
 *
 *       |<-------w------->|
 *       +--------+--------+ L n (top/ceilings)
 *                .
 *                .                       perspective
 *       +--------+--------+ L 1               |
 *       +--------+--------+ L 0 (primary)     |
 *       +--------+--------+ L-1               v
 *                .
 *                .
 *       +--------+--------+ F-m (bottom/floors)
 */

namespace descent { namespace geometry {
/// 
typedef std::vector<std::uint8_t> path;
/// Minimum logarithmic width of a cell. A cell of this width cannot be further subdivided. The absolute minimum width is 2^x, where x is this constant.
const std::uint8_t min_width = 5;
/// Offsets of subdivisions relative to their parent. For example, subdivision 0 is offset upon both the x and y axes (hence the vector [1, 1]).
const boost::array<const mocha::i8vec2, 4> sub_offsets = { { mocha::i8vec2(1, 1), mocha::i8vec2(1, 0), mocha::i8vec2(0, 0), mocha::i8vec2(0, 1) } };
/// Axes upon which edges of a cell are deformed (translated). For example, edge 0 is deformed in the negative x direction (hence the vector [-1, 0]).
const boost::array<const mocha::i8vec2, 4> edge_axes = { { mocha::i8vec2(-1, 0), mocha::i8vec2(0, 1), mocha::i8vec2(1, 0), mocha::i8vec2(0, -1) } };
/// Axes upon which points of a cell are deformed (translated). Points are analogous to edge offsets. For example, point 0 is deformed in both the negative x and negative y directions (hence the vector [-1, -1]).
const boost::array<const mocha::i8vec2, 4> point_axes = { { mocha::i8vec2(-1, -1), mocha::i8vec2(-1, 1), mocha::i8vec2(1, 1), mocha::i8vec2(1, -1) } };
/**
 * Cell subdivision index enumerators.
 */
enum {
	sub_southeast = 0,
	sub_northeast = 1,
	sub_northwest = 2,
	sub_southwest = 3
};
/**
 * Cell edge index enumerators.
 */
enum {
	edge_east = 0,
	edge_north = 1,
	edge_west = 2,
	edge_south = 3
};
/**
 * Cell point index enumerators.
 */
enum {
	point_southeast = 0,
	point_northeast = 1,
	point_northwest = 2,
	point_southwest = 3
};
/**
 * Cell clip flags.
 * @see layer
 */
enum {
	clip_normal = 0x00,
	clip_all = 0x01,
	clip_none = 0x02
};
/**
 * Edge of a cell.
 */
union MOCHA_PACKED edge {
	struct MOCHA_PACKED {
		/// First point.
		unsigned int first : 4;
		/// Second point.
		unsigned int second : 4;
	};
	/// Points composed as an 8-bit integer.
	std::uint8_t edge;
};
BOOST_STATIC_ASSERT((sizeof(edge) == sizeof(std::uint8_t))); // Edge must be one byte in size.
/**
 * Layer of a cell.
 * @see edge
 * @see material
 */
struct layer {
	union MOCHA_PACKED {
		/// Four edges of the cell.
		edge edges[4];
		/// Edges composed as a 32-bit integer.
		std::uint32_t face;
	};
	/**
	 * Flags (bitfield).
	 */
	union MOCHA_PACKED {
		struct MOCHA_PACKED {
			/// Empty flag. When set, geometry data is ignored. This optimizes the empty scenario, since data does not need to be interpreted to determine that the cell is empty.
			unsigned int empty : 1;
			/// Kill flag. When set, player collisions with the cell result in instant death.
			unsigned int kill : 1;
			union MOCHA_PACKED {
				struct MOCHA_PACKED {
					/// Clip all flag. When set, nothing can pass through the bounds of the cell.
					unsigned int clipa : 1;
					/// Clip none flag. When set, anything can pass through the bounds of the cell.
					unsigned int clipn : 1;
				};
				struct MOCHA_PACKED {
					/// Clip flags. Offers an alternate view on the two clip flags.
					unsigned int clip : 2;
				};
			};
		};
		/// Flags composed as an 8-bit integer.
		std::uint8_t flags;
	};
	/// Material.
	material_ptr_t mat;
};
/**
 * Cell data.
 * Separated from cells since only leaves contain data (a pointer to the data is used).
 * @see data_ext
 * @see layer
 */
struct data {
	/// 
	typedef std::map<std::int16_t, layer> layer_con_t;
	/// Layers.
	layer_con_t layers;
	/// Extended data.
	data_ext_ptr_t ext;
};
/**
 * Extended cell data.
 * Provides extended information about a cell. Can be disabled.
 * @see data
 */
struct data_ext {};
/**
 * Cell.
 */
class cell : public boost::enable_shared_from_this<cell> {
public:
	/**
	 * @param width 
	 */
	explicit cell(uint8_t width);
	~cell();
	/**
	 * @param p Receives the path to the cell.
	 */
	void path(path& p) const;
	/**
	 * @param b Receives the bounds of the cell.
	 */
	void bounds(mocha::region<std::int32_t>& b) const;
	/**
	 * @return 
	 */
	std::uint8_t depth() const;
private:
	/// Subdivision container type.
	typedef boost::array<cell, 4> sub_con_t;
	/// Pointer to subdivision container type.
	typedef boost::shared_ptr<sub_con_t> sub_con_ptr_t;
	/**
	 * @param depth 
	 * @param width 
	 * @param x 
	 * @param y 
	 */
	cell(std::uint8_t depth, std::uint8_t width, std::uint16_t x, std::uint16_t y);
	/**
	 * 
	 */
	void init();
	/**
	 * 
	 */
	void subdivide();
	/// 
	std::uint8_t depth_;
	/// Width (logarithmic).
	std::uint8_t width_;
	/// Position (in multiples of absolute width).
	std::uint16_t x_, y_;
	/// Subdivisions. Only available in non-leaves.
	sub_con_ptr_t subs_;
	/// Data. Only available in leaves.
	data_ptr_t data_;
};
/**
 * @param width Logarithmic width.
 * @return Absolute width.
 */
inline std::uint32_t absw(std::uint8_t width) {
	if (0 == width) {
		return 1u;
	}
	return 1u << (width - 1);
}
/**
 * @param width Absolute width. Must be a power of two.
 * @return Logarithmic width.
 */
inline std::uint8_t logw(std::uint32_t width) {
	return 0; // TODO: 
}
}
using ::descent::geometry::cell;
using ::descent::geometry::path;
}

#endif

